package Middleware

import (
	"net"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/gin-gonic/gin"

	"hyper/supports/logger"
	"hyper/supports/response"
)

// Recovery 使用 zap.Error() 来记录 Panic 和 call stack
func Recovery() gin.HandlerFunc {
	return func(c *gin.Context) {

		defer func() {

			if err := recover(); err != nil {

				// 获取用户的请求信息
				httpRequest, _ := httputil.DumpRequest(c.Request, true)

				// 链接中断，客户端中断连接为正常行为，不需要记录堆栈信息
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {

					if se, ok := ne.Err.(net.Error); ok {

						errStr := strings.ToLower(se.Error())
						if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection refused") {
							brokenPipe = true
						}
					}
				}

				// 链接中断的情况
				if brokenPipe {

					logger.Error(response.D{
						"path":    c.Request.URL.Path,
						"err":     err,
						"request": string(httpRequest),
					})
					c.Error(err.(error))
					c.Abort()
					// 链接已断开，无法写状态码
					return

				}

				// 如果不是链接中断，就开始记录堆栈信息
				logger.Error(response.D{
					"Title":   "recovery from panic",
					"Errors":  err,
					"Request": string(httpRequest),
				})

				// 返回 500 状态码
				response.ResponseException(c, http.StatusInternalServerError, "服务器内部错误，请稍后再试", nil)

			}

		}()
		c.Next()
	}
}
